package com.immo.libcommon.utils;

import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * @author Administrator
 * @content toast方法封装，避免toast在android7.1.1不适配问题
 * @date 2018/5/3
 */
public class ToastUtils extends Toast {

    private static final String TAG = "ToastUtils";

    private ToastUtils(Context context) {
        super(context);
    }

    /**
     * Make a standard toast that just contains a text view.
     *
     * @param context  The context to use.  Usually your {@link android.app.Application}
     *                 or {@link android.app.Activity} object.
     * @param text     The text to show.  Can be formatted text.
     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or
     *                 {@link #LENGTH_LONG}
     */
    public static Toast makeText(Context context, CharSequence text, int duration) {
        ToastUtils result = new ToastUtils(context);

        LayoutInflater inflate = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        Resources resources = context.getResources();
        assert inflate != null;
        View v = inflate.inflate(resources.getIdentifier("transient_notification", "layout", "android"), null);
        TextView tv = v.findViewById(resources.getIdentifier("message", "id", "android"));
        tv.setText(text);
        result.setView(v);
        result.setDuration(duration);
        return result;
    }

    @Override
    public void show() {
        if (checkIfNeedToHack()) {
            tryToHack();
        }
        super.show();
    }

    private boolean checkIfNeedToHack() {
        return Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1;
    }

    private void tryToHack() {
        try {
            Object mTN = getFieldValue(this, "mTN");
            if (mTN != null) {
                boolean isSuccess = false;

                //a hack to some device which use the code between android 6.0 and android 7.1.1
                Object rawShowRunnable = getFieldValue(mTN, "mShow");
                if (rawShowRunnable != null && rawShowRunnable instanceof Runnable) {
                    isSuccess = setFieldValue(mTN, "mShow", new InternalRunnable((Runnable) rawShowRunnable));
                }

                // hack to android 7.1.1,these cover 99% devices.
                if (!isSuccess) {
                    Object rawHandler = getFieldValue(mTN, "mHandler");
                    if (rawHandler != null && rawHandler instanceof Handler) {
                        isSuccess = setFieldValue(rawHandler, "mCallback", new InternalHandlerCallback((Handler) rawHandler));
                    }
                }

                if (!isSuccess) {
                    Log.e(TAG, "tryToHack error.");
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private class InternalRunnable implements Runnable {
        private final Runnable mRunnable;

        InternalRunnable(Runnable mRunnable) {
            this.mRunnable = mRunnable;
        }

        @Override
        public void run() {
            try {
                this.mRunnable.run();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    private class InternalHandlerCallback implements Handler.Callback {
        private final Handler mHandler;

        InternalHandlerCallback(Handler mHandler) {
            this.mHandler = mHandler;
        }

        @Override
        public boolean handleMessage(Message msg) {
            try {
                mHandler.handleMessage(msg);
            } catch (Throwable e) {
                e.printStackTrace();
            }
            return true;
        }
    }

    private static boolean setFieldValue(Object object, String fieldName, Object newFieldValue) {
        Field field = getDeclaredField(object, fieldName);
        if (field != null) {
            try {
                int accessFlags = field.getModifiers();
                if (Modifier.isFinal(accessFlags)) {
                    Field modifiersField = Field.class.getDeclaredField("accessFlags");
                    modifiersField.setAccessible(true);
                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
                }
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                field.set(object, newFieldValue);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private static Object getFieldValue(Object obj, final String fieldName) {
        Field field = getDeclaredField(obj, fieldName);
        return getFieldValue(obj, field);
    }

    private static Object getFieldValue(Object obj, Field field) {
        if (field != null) {
            try {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                return field.get(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static Field getDeclaredField(final Object obj, final String fieldName) {
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(fieldName);
            } catch (NoSuchFieldException ignored) {

            }
        }
        return null;
    }


    /**
     * 显示toast
     *
     * @param context 上下文
     * @param msg     显示内容
     */
    public static void showToast(Context context, String msg) {
        ToastUtils.makeText(context, msg, ToastUtils.LENGTH_SHORT).show();

    }

}
